package cn.zxf.note.security;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import jakarta.validation.constraints.NotNull;

import java.util.Map;
import java.util.Objects;

/**
 * Created by zengxf on 2019/4/9.
 */
@Slf4j
@Component
public class SecurityWebFilter implements WebFilter, SecurityConstant {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        log.info("{} request: {}", request.getRemoteAddress(), path);

        if (NO_AUTH_PATHS.contains(path))
            return chain.filter(exchange);

        Integer userId = this.getUserId(request);
        if (userId != null) {
            request = request.mutate()
                    .header(KEY_CUR_USER_ID, userId.toString())
                    .build();
            ServerWebExchange newEx = exchange.mutate()
                    .request(request)
                    .build();
            return chain.filter(newEx);
        }
        return this.errorResponse(exchange);
    }

    private Integer getUserId(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(KEY_AUTH_TOKEN);
        Integer userId = TokenUtils.parse(token);
        if (userId != null)
            return userId;

        String uidParam = request.getQueryParams().getFirst(KEY_CUR_USER_ID);
        if (uidParam == null)
            return null;

        String[] arr = uidParam.split(PARAM_SEPARATOR);
        if (arr.length != 2)
            return null;

        String uid = arr[0];
        String uidEncrypt = arr[1];
        if (!Objects.equals(uid, SecretUtils.decrypt(uidEncrypt)))
            return null;

        return Integer.valueOf(uid);
    }

    private Mono<Void> errorResponse(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();
        String json = toJson(Map.of("error", "1", "reason", "token invalid"));
        byte[] content = json.getBytes();
        DataBuffer wrap = response.bufferFactory().wrap(content);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        headers.add("Content-Type", "application/json;charset=UTF-8");
        headers.add("Content-Length", content.length + "");
        return response.writeWith(Mono.just(wrap));
    }

    @NotNull
    private static String toJson(Map<String, Object> map) {
        try {
            return new ObjectMapper().writeValueAsString(map);
        } catch (JsonProcessingException e) {
            log.error("to json error! map: {}", map, e);
        }
        return "";
    }

}
